#include <iostream>
#include <fstream>
#include <Poco/AutoPtr.h>
#include "Poco/Util/IniFileConfiguration.h"
#include "Poco/Exception.h"
#include "File/File.h"
#include "Ini.h"

using namespace SystemTool;

class IniCfg : public Poco::Util::IniFileConfiguration
{
public:
    IniCfg(){ }

    // Save change to ini file
    bool SaveConfig(std::string strIniFilePath)
    {
        using Poco::Util::IniFileConfiguration;

        std::fstream os(strIniFilePath,std::ios::out | std::ios::trunc);
        if (!os.is_open())
        {
            return false;
        }

        try
        {
            std::string strRootKey;
            std::vector<std::string> vRootRange;
            // If an empty key is passed, all root level keys are returned.
            enumerate(strRootKey, vRootRange);
            if (vRootRange.empty())
            {
                return false;
            }

            std::vector<std::string> vSubRange;
            for (size_t index = 0; index < vRootRange.size(); index++)
            {
                // Write section key
                std::string strSecKey = vRootRange[index];
                enumerate(strSecKey, vSubRange);
                std::string FinalSecKey = "[" + strSecKey + "]" + "\n";
                os << (char *)FinalSecKey.c_str();

                // Write key-value under the section key
                for (auto valueKey : vSubRange)
                {
                    std::string pair = valueKey;
                    pair.append("=");
                    pair.append(getString(strSecKey + "." + valueKey));
                    std::string result(pair.begin(), pair.end());
                    result += "\n";
                    os << (char *)result.c_str();
                }
                vSubRange.clear();
            }
            os.close();
        }
        catch (Poco::Exception &e)
        {
            os.close();
            std::cout << "Failed to save new ini file:" << e.displayText() << std::endl;
        }
        return true;
    }
};

Poco::AutoPtr<IniCfg> g_Configurator(new IniCfg);

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, std::string &strDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetString(strFilePath, strDataKey, strDefaultValue);
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, int &iDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetInt(strFilePath, strDataKey, iDefaultValue);
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned int &iDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetUInt(strFilePath, strDataKey, iDefaultValue);
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, short &iDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetInt16(strFilePath, strDataKey, iDefaultValue);
}

// Get data by key
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned short &iDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetUInt16(strFilePath, strDataKey, iDefaultValue);
}

// Get data by key
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, long long &lDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetInt64(strFilePath, strDataKey, lDefaultValue);
}

// Get data by key
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned long long &ulDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetUInt64(strFilePath, strDataKey, ulDefaultValue);
}

// Get data by key
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, double &dDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetDouble(strFilePath, strDataKey, dDefaultValue);
}

// Get data by key
bool Ini::GetKey(std::string strFilePath, std::string strAppKey, std::string strKey, bool &bDefaultValue)
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    return GetBool(strFilePath, strDataKey, bDefaultValue);
}

// Get string by key(Key format: SectionKey.ValueKey)
bool Ini::GetString(std::string strIniFilePath, std::string strKey, std::string &strValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        strValue = g_Configurator->getString(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetInt(std::string strIniFilePath, std::string strKey, int &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getInt(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetUInt(std::string strIniFilePath, std::string strKey, unsigned int &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }
    
    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getUInt(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetInt16(std::string strIniFilePath, std::string strKey, short &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }
    
    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getInt16(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetUInt16(std::string strIniFilePath, std::string strKey, unsigned short &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getUInt16(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetInt64(std::string strIniFilePath, std::string strKey, long long &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getInt64(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetUInt64(std::string strIniFilePath, std::string strKey, unsigned long long  &iValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }
    
    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        iValue = g_Configurator->getUInt64(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetDouble(std::string strIniFilePath, std::string strKey, double &dValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }
    
    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        dValue = g_Configurator->getDouble(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return false;
}

// Get data by key(Key format: SectionKey.ValueKey)
bool Ini::GetBool(std::string strIniFilePath, std::string strKey,bool& bValue)
{
    if(strIniFilePath.empty())
    {
        return false;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(!g_Configurator->has(strKey))
        {
            return false;
        }
        bValue = g_Configurator->getBool(strKey);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, std::string strDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetString(strFilePath, strDataKey, strDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, int iDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetInt(strFilePath, strDataKey, iDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned int iDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetUInt(strFilePath, strDataKey, iDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, short iDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetInt16(strFilePath, strDataKey, iDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned short iDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetUInt16(strFilePath, strDataKey, iDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, long long lDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetInt64(strFilePath, strDataKey, lDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, unsigned long long ulDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetUInt64(strFilePath, strDataKey, ulDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, double dDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetDouble(strFilePath, strDataKey, dDefaultValue);
    return true;
}

// Set data with key value
bool Ini::SetKey(std::string strFilePath, std::string strAppKey, std::string strKey, bool bDefaultValue)
{
    if(strFilePath.empty())
    {
        return false;
    }
    if(strAppKey.empty())
    {
        return false;
    }
    if(strKey.empty())
    {
        return false;
    }

    std::string strDataKey = strAppKey + "." + strKey;
    SetBool(strFilePath, strDataKey, bDefaultValue);
    return true;
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetString(std::string strIniFilePath, std::string strKey, std::string strValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }
    
    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setString(strKey,strValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetInt(std::string strIniFilePath, std::string strKey,  int iValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setInt(strKey,iValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetUInt(std::string strIniFilePath, std::string strKey,  unsigned int uValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setUInt(strKey,uValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetInt16(std::string strIniFilePath, std::string strKey,  short sValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setInt16(strKey,sValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetUInt16(std::string strIniFilePath, std::string strKey,  unsigned short uValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setUInt16(strKey,uValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetInt64(std::string strIniFilePath, std::string strKey,  long long lValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setInt64(strKey,lValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetUInt64(std::string strIniFilePath, std::string strKey,  unsigned long long ulValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setUInt64(strKey,ulValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetDouble(std::string strIniFilePath, std::string strKey,  double dValue)
{   
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setDouble(strKey,dValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}

// Set data by key(Key format: SectionKey.ValueKey)
void Ini::SetBool(std::string strIniFilePath, std::string strKey,  bool bValue)
{
    if(strIniFilePath.empty())
    {
        return;
    }

    if(!SystemTool::File::IsExisted(strIniFilePath))
    {
        SystemTool::File::Create(strIniFilePath);
    }

    try
    {
        g_Configurator->load(strIniFilePath);
        if(strKey.empty())
        {
           return; 
        }
        g_Configurator->setBool(strKey,bValue);
        g_Configurator->SaveConfig(strIniFilePath);
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Err Happend:" << e.displayText() << std::endl;
    }
}
